#include "malloc.h"


//Memory pool(4 bytes aligned)
__align(4) u8 inmenbase[INMEM_MAX_SIZE];									//Internal SRAM memory pool
__align(4) u8 exmen2base[EXMEM_MAX_SIZE] __attribute__((at(0X68000000)));	//External SRAM memory pool
//Memory management table
u16 inmemmapbase[INMEM_ALLOC_TABLE_SIZE];												    //Internal SRAM MAP
u16 exmemmapbase[EXMEM_ALLOC_TABLE_SIZE] __attribute__((at(0X68000000+EXMEM_MAX_SIZE)));	//External SRAM MAP
//Memory management parameters	   
const u32 memtblsize[2]={INMEM_ALLOC_TABLE_SIZE,EXMEM_ALLOC_TABLE_SIZE};		//Memory table size
const u32 memblksize[2]={INMEM_BLOCK_SIZE,EXMEM_BLOCK_SIZE};					//Memory block size
const u32 memsize[2]={INMEM_MAX_SIZE,EXMEM_MAX_SIZE};							//Total memory size



u8 	*membase[2]={inmenbase,exmen2base};			//Memory pool, manages the memory of 2 areas
u16 *memmap[2]={inmemmapbase,exmemmapbase};     //Memory management status table
u8  memrdy[2]={0,0}; 						    //Is memory management ready


/**
 * @brief Copy memory	
 *
 * @param  * des: destination address
 * @param  * src: source address
 * @param  n: length of memory to be copied (bytes)
 *
 * @return  
 */
void mymemcpy(void *des,void *src,u32 n)  
{  
    u8 *xdes=des;
	  u8 *xsrc=src; 
    while(n--)*xdes++=*xsrc++;  
}  


/**
 * @brief Copy memory	
 *
 * @param  * s: memory first address
 * @param  c: value to be set
 * @param  n: length of memory to be copied (bytes)
 *
 * @return  void
 */
void mymemset(void *s,u8 c,u32 count)  
{  
    u8 *xs = s;  
    while(count--)*xs++=c;  
}	 

// Initialize memory management
// memx: belongs to the memory block
void Memory_Init(u8 memx)  
{  
  mymemset(memmap[memx], 0,memtblsize[memx]*2);  
	mymemset(membase[memx], 0,memsize[memx]);	    
	memrdy[memx]=1;								 
}  


/**
 * @brief Get memory usage	
 *
 * @param  memx: memory block
 *
 * @return  usage rate (0 ~ 100)
 */
u8 Mem_perused(u8 memx)  
{  
    u32 used=0;  
    u32 i;  
    for(i=0;i<memtblsize[memx];i++)  
    {  
        if(memmap[memx][i])used++; 
    } 
    return (used*100)/(memtblsize[memx]);  
}  


/**
 * @brief Memory allocation (internal call)	
 *
 * @param  memx: memory block
 * @param  size: size of memory to be allocated (bytes)
 *
 * @return  0XFFFFFFFF: representing error; other: memory offset address
 */
u32 memin_malloc(u8 memx,u32 size)  
{  
    signed long offset=0;  
    u16 nmemb;	  
	  u16 cmemb=0;
    u32 i;  
	
    if(!memrdy[memx])Memory_Init(memx); 
    if(size==0)return 0XFFFFFFFF;

    nmemb=size/memblksize[memx];  	
    if(size%memblksize[memx])nmemb++;  
    for(offset=memtblsize[memx]-1;offset>=0;offset--)
    {     
		if(!memmap[memx][offset])cmemb++;
		else cmemb=0;								
		if(cmemb==nmemb)							
		{
            for(i=0;i<nmemb;i++)  					 
            {  
               memmap[memx][offset+i]=nmemb;  
            }  
            return (offset*memblksize[memx]);  
		}
    }  
    return 0XFFFFFFFF; 
}  


/**
 * @brief Memory free (internal call)
 *
 * @param  offset: memory address offset
 *
 * @return  0: successful release; 1: failed release;
 */
u8 memin_free(u8 memx,u32 offset)  
{  
    int i;  
    if(!memrdy[memx])
	{
		Memory_Init(memx);    
        return 1;  
    }  
    if(offset<memsize[memx]) 
    {  
        int index=offset/memblksize[memx];			
        int nmemb=memmap[memx][index];	
        for(i=0;i<nmemb;i++)  						
        {  
          memmap[memx][index+i]=0;  
        }  
        return 0;  
    }else return 2;
}  


/**
 * @brief Memory free
 *
 * @param  memx: internal and external memory selection  0: internal 1: external
 * @param  use: the first address of the previously allocated memory
 *
 * @return  void
 */
void Mem_free(u8 memx,void *use)  
{  
	u32 offset;  
    if(use==NULL)return;  
 	  offset=(u32)use-(u32)membase[memx];  
    memin_free(memx,offset);    
}  


/**
 * @brief Memory allocation
 *
 * @param  memx: internal and external memory selection 0: internal 1: external
 * @param  size: the size of memory to be allocated
 *
 * @return  The first address of the allocated memory
 */
void *Mem_malloc(u8 memx,u32 size)  
{  
    u32 offset;  									      
	  offset=memin_malloc(memx,size);  	   				   
    if(offset==0XFFFFFFFF)return NULL;  
    else return (void*)((u32)membase[memx]+offset);  
}  


/**
 * @brief Reallocate memory
 *
 * @param  memx: internal and external memory selection 0: internal 1: external
 * @param  old: the first address of the old memory
 * @param  size: need to reallocate the size of memory
 *
 * @return  The first address of the allocated memory
 */
void *Remem_malloc(u8 memx,void *old,u32 size)  
{  
    u32 offset;  
    offset=memin_malloc(memx,size);  
    if(offset==0XFFFFFFFF)return NULL;     
    else  
    {  									   
	    mymemcpy((void*)((u32)membase[memx]+offset),old,size);	   
      Mem_free(memx,old);  									    
      return (void*)((u32)membase[memx]+offset);  				
    }  
} 

